不知道你有沒有注意過早上的起床順序呢,像是作者本來起床的順序是:
有注意到嗎這些事情都是一個一個執行,相信應該沒有人可以一邊刷牙一邊喝咖啡吧!
人腦是如此,電腦也不意外同時間一個執行緒能做的也就是一件事情,
JS 之中要做到有順序的處理 Function 其中有幾個方式 callback function、Promise、 為什麼會發生 callback Hell,在本章節都會提到!
廢話不多說我們一起往下走吧!
我們先來介紹不可不知道的 Callback Function。
當我們在一般撰寫 Funciton 時,呼叫三件事情會是這樣呼叫的。
const A = function () {}
const B = function () {}
const C = function () {}
A()
B()
C()
可以看到執行順序是 A() -> B() -> C()。
原因是這三個 Function 都是立即執行所以會按照順序完成。
但如果這三件事情有一個 Function 出現了延遲會發生什麼事情呢?
const A = function () { setTimeOut(()=>{},1000)}
const B = function () {}
const C = function () {}
A()
B()
C()
執行順序變成 B() -> C() -> A()。
那如果我們想要讓執行順序變 C()-> A() -> B() ,那 Callback Function 會是您的好選擇。
Callback Function 主要帶來的效益是 可以控制執行順序的程式
。
而什麼是 Callback Function 呢
Callback: 將一 Function 透過
參數
的方式傳進另一 Function 之中。
Callback Function:被當成參數傳遞
的 Function 稱作 Callback Function。
讓可以直接看到例子。
/**
* params {object} Breakfask
* params {function} TakeBus
*/
const eatBreakfast = (breakfast, takeBus) => {
//- 萬惡小黃瓜不吃了不上班了!
if(小黃瓜 in breakfast) return
takeBus()
}
//- takeBus 就是 Callback Function
在了解 Callback Function 之後,我們可以改寫一下上面的例子,我們預期執行順序: C()-> A() -> B() 。
const A = function (func) { setTimeOut(()=>{func()},1000)}
const B = function () {}
const C = function () {}
A(B())
C()
這樣就可以做到你想的事情摟!! 這樣的作法經常被應用於 Ajax,因為 Ajax 是向外部進行請求時需等待資訊,並判斷下一步應該透過哪一個 Function 來進行處理。
但如果今天 CallBack Function 非常的多層就會看到這樣哩。
function helloIsme() {
console.log("Hello is me");
}
doSomething(result => {
doSomething(result, (_r) => {
doSomething(_r, (_fr) => {
console.log("Here's Johnny")
}, helloIsme);
}, helloIsme);
}, helloIsme);
Johnny 就會出現(誤)
沒有拉 Johnny 不會出現只會有維護這段程式碼的人會出現,當層數變多還可以順便打波動拳呢,終於可以用上這張圖片了。
這樣的多層結構被稱作於 Callback Hell,這樣就讓維運/接手人員折壽,所以 Promise 這東西出來拯救世人了。
Promise 物件代表一個即將完成、或失敗的非同步操作,以及它所產生的值。 引用自Promise - JavaScript | MDN
換句話說 Promise 是一個非同步程序,當程序完成並成功時可以接應成功資料,反之失敗時可以接應失敗的物件。
那上面提到的 callback function 產生的問題 callback hell 就可透過狀態進行反饋。
doSomething((result) => {
doSomething(result, (_r) => {
doSomething(_r, (_fr) => {
console.log("Here's Johnny")
}, helloIsme);
}, helloIsme);
}, helloIsme);
//- 就會變成
doSomething()
.then(()=>{reutrn doSomething()})
.then(()=>{return doSomething()})
.then(()=>{ console.log("Here's Johnny") })
.catch(()=>{})
是不是變的相當簡潔呢。
在學習前我們來了解一下 Promise 物件,此物件在執行中會有以下幾種狀態:
擱置(pending):初始狀態,不是 fulfilled 與 rejected。
實現(fulfilled):表示操作成功地完成。
拒絕(rejected):表示操作失敗了。
在 Promise 結果為 fulfilled 時代表 Promise resolve,透過 .then() 的方式可以編寫成功該做何事。
在 Promise 結果為 rejected 時代表 Promise reject,透過 .catch() 的方式可以編寫失敗該做何事。
Promise 是建構函式
,需要透過 new Promise()
來進行建置,除此之外還需要給予 Function ,告知 Promise 需如何進行成功與失敗的判斷,function 內容必須有兩個參數 resolve
, reject
,分別代表成功與失敗,可以看見下方例子。
const _promise = new Promise((resolve,reject)=>{
//- todo something 成功
resolve()
//- todo something 失敗
reject()
})
* resolve 為成功取得的資料時,回傳資料。
* reject 則會在失敗時回傳 error object。
使用上會有幾個 function 來表達此 Promise 狀態。
.then()
: 接獲 resolve 結果時會執行。.catch()
: 接獲 reject 結果時會執行。.finally()
: 當此 Promise 所有 .then / .catch 完成後執行。_promise
.then((res)=>{console.log('todo success thing')})
.catch((err)=>{console.log('todo error thing')})
.finally(()=>{console.log('todo finally thing')})
如果文字上看得不太清楚的話可以直接看下圖。
再來我想要特別提到 Promise 中很重要的概念,鏈結(Promise Chain)
,如果想要使用鏈結的話需要在 then 中進行 return 一個 Promise,而下一個的 then 就會是 return 的結果。
需要特別注意: Promise 可以有很多的 then ,但是只能有一個 catch 。
透過此方法來建置 Promise 的話,此 Promise 結果已經決定。
Promise.resolve 此結果必定成功。
Promise.reject 此結果必定失敗。
const _promise = new Promise((resolve,reject)=>{
resolve('success')
reject('error')
})
//- 只會成功不會失敗 'success'
_promise.resolve((res)=>{ console.log(res.data) })
//- 只會失敗不會成功 'error'
_promise.reject((err)=>{ console.log(err) })
此方法用於多個 Promise 一同執行,並且等待所有 Promise 後才會一同返回,注意當有一個 Promise reject ,就會進入 catch ,必須全部回傳成功(resolve)才會進入 then()。。
const A = new Promise((resolve,rejcet)=>{resolve(1)})
const B = new Promise((resolve,rejcet)=>{resolve(2)})
const C = new Promise((resolve,rejcet)=>{resolve(3)})
const D = new Promise((resolve,rejcet)=>{reject('error')})
Promise.all([A,B,C])
.then(([resA,resB,resC])=>{
console.log(resA,resB,resC)
})
.catch(error=>{console.log(error)})
//- console: 1, 2, 3
Promise.all([A,B,D])
.then(([resA,resB,resD])=>{
console.log(resA,resB,resD)
})
.catch(err=>{
console.log(error)
})
//- 'error'
也因為有一個 reject 就會進入 catch,所以會沒有辦法知道是哪一個 Promise 錯誤導致無法偵錯。
雖與 Promise.all 一樣是,多個 Promise 進行同時處理,並等待完成後一起回傳。
每一個回傳都會包含 {status,value,reason}
const A = new Promise((resolve,rejcet)=>{resolve(1)})
const B = new Promise((resolve,rejcet)=>{resolve(2)})
const C = new Promise((resolve,rejcet)=>{resolve(3)})
const D = new Promise((resolve,rejcet)=>{reject('error')})
Promise.all([A,B,C,D])
.then((res)=>{
console.log(res)
})
.catch(error=>{console.log(error)})
//- console: { status: 'fulfilled', value: '1' }, { status: 'fulfilled', value: '2' }, { status: 'fulfilled', value: '3' },{ status: 'rejected', reason: 'error' }
Q1. Ajax 與 Promise 的差異?
Ans: Ajax 是處理 Client 與 Server 之間的溝通方式; Promise 是處理非同步的語法。舉個例子來比喻 Ajax 是人與人的溝通技巧,Promise 則是人溝通所說的語言。